package com.mxx.common.tool;

import com.mxx.common.core.BeanMerge;
import com.mxx.common.core.EmptyFunction;
import com.mxx.common.thread.BeanTag;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.Timer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @Auther: Mr. Zeng
 * @Date: 2020/6/6 15:00
 * @Description: 暂无描述
 */
@Slf4j
public final class LocalStorage<T extends BeanTag> {
    private ConcurrentHashMap<String, T> contentStorage = new ConcurrentHashMap<>();
    private Supplier<List<T>> contentReload;
    private Function<String, T> contentUpdate;
    private TimerTask timerTask;
    private java.util.Timer timer;


    public LocalStorage(Supplier<List<T>> contentReload) {
        this.contentReload = contentReload;
        this.contentUpdate = EmptyFunction::EMPTY_FUNCTION;
    }

    public LocalStorage(Supplier<List<T>> contentReload, Function<String, T> contentUpdate) {
        this.contentReload = contentReload;
        this.contentUpdate = contentUpdate;
    }

    public LocalStorage<T> reload(BeanMerge<Map<String, T>> merge) {
        if (merge == null) {
            throw new NullPointerException("MapMerge must not null");
        }
        List<T> contextLis = this.contentReload.get();
        Map<String, T> newData = new HashMap();
        for (T t : contextLis) {
            newData.put(t.getTag(), t);
        }
        Map<String, T> oldData = new HashMap<>();
        oldData.putAll(contentStorage);
        Map<String, T> saveData = merge.merge(newData, oldData);
        contentStorage.clear();
        contentStorage.putAll(saveData);
        return this;
    }

    public LocalStorage<T> reload() {
        List<T> contextLis = this.contentReload.get();
        Map<String, T> newData = new HashMap();
        for (T t : contextLis) {
            newData.put(t.getTag(), t);
        }
        contentStorage.clear();
        contentStorage.putAll(newData);
        return this;
    }

    public Optional<T> get(String key) {
        return Optional.ofNullable(contentStorage.get(key));
    }

    public void remove(String key) {
        contentStorage.remove(key);
    }

    public LocalStorage<T> flashContext(String key) {
        T t = contentUpdate.apply(key);
        if (t == null) {
            contentStorage.remove(key);
        } else {
            contentStorage.put(key, t);
        }
        return this;
    }

    public LocalStorage<T> flashContext(String key, BeanMerge<T> beanMerge) {
        T t = contentUpdate.apply(key);
        if (t == null) {
            contentStorage.remove(key);
        } else {
            T oldT = contentStorage.get(key);
            contentStorage.put(key, oldT != null ? beanMerge.merge(t, oldT) : t);
        }
        return this;
    }

    public LocalStorage<T> flashContext(String key, T data) {
        contentStorage.put(key, data);
        return this;
    }

    /**
     * 定时 重新加载
     *
     * @param delay  延迟时间
     * @param period 间隔时间
     */

    public void cycleReload(int delay, long period, BeanMerge<Map<String, T>> dataExchange) {
        synchronized (LocalStorage.class) {
            if (timerTask == null) {
                timerTask = new CycleReload(this, dataExchange);
                timer = new java.util.Timer();
            } else {
                ((CycleReload) timerTask).setDataExchange(dataExchange);
            }
        }
        timer.scheduleAtFixedRate(timerTask, delay, period);
    }

    /**
     * 定时 重新加载
     *
     * @param delay  延迟时间
     * @param period 间隔时间
     */

    public void cycleReload(int delay, long period) {
        synchronized (LocalStorage.class) {
            if (timerTask == null) {
                timerTask = new CycleReload(this, null);
                timer = new Timer();
            }
        }
        timer.scheduleAtFixedRate(timerTask, delay, period);
    }


    private class CycleReload extends TimerTask {
        private com.mxx.common.tool.LocalStorage<T> LocalStorage;
        private BeanMerge<Map<String, T>> merge;

        public CycleReload(com.mxx.common.tool.LocalStorage<T> LocalStorage, BeanMerge<Map<String, T>> merge) {
            this.LocalStorage = LocalStorage;
            this.merge = merge;
        }

        public void setDataExchange(BeanMerge<Map<String, T>> merge) {
            this.merge = merge;
        }

        @Override
        public void run() {
            try {
                if (merge == null) {
                    LocalStorage.reload();
                } else {
                    LocalStorage.reload(merge);
                }
            } catch (Exception ex) {
                log.error("LocalStorage reload config exception ", ex);
            }
        }
    }
}
